/**
 * Tabs Widget UI Plugin (functionality for custom tabbed-form widgets)
 * 
 * @version 0.1
 * @author Trungtin Ton
 * @dependency jQuery v1.7.1 +
 */
(function($) {
	"use strict";

	var functions = undefined, methods = undefined;
	/*
	 * Private functions within the scope of this plugin that define all of the
	 * necessary low-level operations required by in order to implement the
	 * plugin's API.
	 */
	functions = {
		/**
		 * Safely initiates and executes a swap operation to the specified form
		 * and updates the tab-set to indicate a change to the newly selected
		 * tab specified.
		 * 
		 * @param id
		 *            the id attribute of the target form. This id must be given
		 *            as a string.
		 * @param a
		 *            the id attribute of the tab that was selected. this
		 *            attribute must be specified as a string. In most cases
		 *            this should be associated with an anchor element, or
		 *            otherwise any other click-able element that is supports
		 *            the JavaScript "click" event in many different browser
		 *            versions.
		 * 
		 */
		swapTo : function(formId, a) {
			/*
			 * ******************************************************* SWAP
			 * function DEV NOTES
			 * *******************************************************
			 * 
			 * TODO: Need to prevent concurrent swap operations.
			 * 
			 * @task Implement a mechanism that blocks any further swapTo
			 * operations whilst the current swap is executing. The goal is to
			 * synchronize swap operations over a lock that controls access to
			 * swap assets and freezes them when a swap is initiated to prevent
			 * concurrency issues.
			 * 
			 * @note The best way to design this would be to create an operation
			 * pipeline (or queue) that only allows one swap to occur at a time
			 * and queues up consecutive swaps for later execution, and delegate
			 * that pipeline to a parent handler. Whenever a swap event is
			 * triggered the event is pushed to the delegate's swap handler,
			 * which then decides what to do with the swap event. It may also be
			 * highly desirable to then implement a simple abort mechanism that
			 * allows the immediate termination of queued-up swaps (but probably
			 * not the currently executing one - we never want a swap to be
			 * killed halfway leaving the widget in an orphaned and unstable
			 * proxy state. We only allow it to either finish or rollback
			 * completely).
			 * 
			 * @note This can then also be used internally by the delegate to
			 * allow only one swap (the most recently invoked one) to be in the
			 * queue at a time, by forcing queued swaps off the line. This will
			 * be necessary to prevent the chaining of many swap events from
			 * causing a redundant build up in the queue. In other words, if the
			 * user has clicked on another tab, it is highly likely they have
			 * changed their mind and do not want to view the previous form
			 * anymore. Moreover, it can lead to serious lag and long wait-times
			 * for a long queue of operations to empty - which if not
			 * controlled, could be an indefinitely long time and could
			 * practically freeze the ui.
			 */
			var parent, tabs, forms, activeForm, targetForm, proxy;
			/*
			 * @note the following are READ-only operations. These must be
			 * synchronized as they are time and context dependent.
			 */
			parent = $(a).parents(".frontforms");
			tabs = parent.children(".tabset_tabs");
			forms = parent.children(".fields");
			activeForm = forms.children(".tabset_content.active_form");
			targetForm = $("#" + formId);

			if (activeForm && activeForm.attr("id") === formId) {
				// already selected, don't need to do anything
				return true;

			} else {
				/*
				 * @note the following block of code contains READ and WRITE
				 * operations.
				 */
				// Add a ui-proxy to be animated to the target position
				proxy = $("<fieldset />", {
					"class" : activeForm.attr("class") + " ui-animation-proxy"
				}).css({
					"width" : activeForm.css("width"),
					"height" : activeForm.css("height")
				}).insertAfter(activeForm.first());
				/*
				 * @note as soon as the underlying function collapses the
				 * currently active form, the operation enters a state of
				 * volatility and remains unstable until there is once again an
				 * active form and the original state is restored. During this
				 * time, swapping resources and assets must be synchronized, or
				 * there is an extreme risk of having concurrency issues.
				 */
				// collapse the currently open form
				activeForm.toggleClass("active_form");
				proxy.animate({
					"height" : targetForm.css("height")
				}, {
					// known issues with tab behavior occur when duration of
					// animation is set higher than zero (0). Issues appear to
					// be less likely to occur if effects are reduced.
					duration : 100,
					complete : function() {
						// expand the target form
						targetForm.css({
							"visibility" : "visible"
						}).fadeIn(200, function() {
							$(this).css({
								"display" : "",
								"visibility" : ""
							}).toggleClass("active_form");
						});
						$(this).remove(); // destroys the animation proxy
						tabs.children(".selected").toggleClass("selected");
						$(a).parent().toggleClass("selected");
					}
				});
			}
		}
	},

	/*
	 * Methods that are exposed through the plugin's public API.
	 */
	methods = {
		/**
		 * Binds the specified tab ID to the subsequently specified form ID. The
		 * IDs may optionally be provided as a key-value mapping of tabs to
		 * forms respectively. In this case the mapping should be passed into
		 * the first and only argument. In this case the method will ignore the
		 * second argument.
		 * 
		 * @param arg0
		 *            either the string ID selector of the tab to be bound to
		 *            the specified form, or a mapping containing key-value
		 *            pairs of tab and form IDs.
		 * @param form
		 *            the string ID selector of the form to be bound to the tab
		 *            ID specified by the previous argument. If this argument is
		 *            supplied, then the previous argument is required to also
		 *            be a string ID selector.
		 * 
		 */
		_bindUITabHandlers : function(arg0, form) {
			var mapping = undefined, tab = undefined, msg = undefined;
			if (form) {
				if (typeof arg0 === "string" && typeof form === "string") {
					tab = arg0;
					$("#" + tab).bind("click.tabs", function(e) {
						functions.swapTo(form, this);
					});
				} else {
					msg = "function requires two string id arguments.";
					console.error("IllegalArgumentException: " + msg);
				}
			} else {
				if (!arg0) {
					msg = "the first argument must not be " + arg0;
					console.error("Incorrect number of arguments: " + msg);
				}
				if (typeof arg0 === "object") {
					mapping = arg0;
					$.each(mapping, function(tab, form) {
						$("#" + tab).bind("click.tabs", function(e) {
							functions.swapTo(form, this);
						});
					});
				} else {
					msg = "argument should be a mapping of strings";
					console.error("IllegalArgumentException: " + msg);
				}
			}
			return this;
		},

		/**
		 * Initialises the ui.tabs jQuery plugin.
		 * 
		 * @param data
		 * @returns {___anonymous3635_5861}
		 */
		init : function(data) {
			var tabbedForm = $("<div />", {
				"class" : "ui-tabs frontforms",
				"html" : $("<ul />", {
					"class" : "ui-tabs tabset_tabs"
				}).after($("<div />", {
					"class" : "ui-tabs fieldset_forms fields"
				}))
			}).appendTo(this),

			tabset = tabbedForm.children(".ui-tabs.tabset_tabs"),

			tab = $("<li />", {
				"class" : "ui-tabs tab",
				"html" : $("<a />", {
					"href" : "#rheet"
				})
			}),

			fields = tabbedForm.children(".ui-tabs.fields"),

			form = $("<fieldset />", {
				"class" : "ui-tabs fieldset_form tabset_content"
			});

			$.each(data, function(tabId, tabOptions) {
				if (tabOptions) {
					var newTab = tab.clone(), newForm = form.clone();

					newTab.children("a").attr({
						"id" : tabId
					}).html(tabOptions.title).parent().appendTo(tabset);

					newForm.attr({
						"id" : tabOptions.form.id,
						"html" : tabOptions.form.content
					}).html(tabOptions.form.content).appendTo(fields);

					methods._bindUITabHandlers(tabId, tabOptions.form.id);

					if (tabOptions.selected) {
						newTab.addClass("selected");
						newForm.addClass("active_form");
					}
				}
			});
			return this;
		}
	};

	/**
	 * Tabbed-forms custom UI jQuery Plugin.
	 * 
	 * @param method
	 *            the method to be called, along with any number of optional
	 *            arguments that will be passed in with the method invocation.
	 *            If the argument is not specified as a method or is left
	 *            undefined, then this invocation is treated as an
	 *            initialisation of the plugin and this argument, if applicable,
	 *            is used in the initialisation parameters.
	 * 
	 */
	$.fn.tabbedform = function(method) {
		// Method calling logic
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(
					arguments, 1));

		} else if (typeof method === 'object' || !method) {
			return methods.init.apply(this, arguments);

		} else {
			$.error('Method ' + method + ' does not exist on jQuery.tooltip');
		}
	};
})(jQuery);